package com.hjxr.quota.util;

import java.math.BigDecimal;
import java.util.*;

public class SimpleFormulaAnalysis {

    private final static int NUMBER = 1;
    private final static int OPERATOR = 2;
    private final static int END_PAREN = 3;
    private final static int START_PAREN = 4;

    /**
     * 操作符优先级
     */
    private static Map<Character, Integer> priority;
    static{
        priority = new HashMap<Character, Integer>(4);
        priority.put('-', 1);
        priority.put('+', 1);
        priority.put('*', 2);
        priority.put('/', 2);
    }

    /**
     * 后缀表达式
     */
    public List<String> postfix = new ArrayList<String>();


    /**
     * 构造
     * @param formula 公式
     */
    public SimpleFormulaAnalysis(String formula){
        formula = formula.toLowerCase();
        LinkedList<Character> stack = new LinkedList<Character>();
        int type = 0;
        StringBuffer str= new StringBuffer();
        for(int i = 0; i < formula.length(); i++){
            char c = formula.charAt(i);
            if(c >= 48 && c <= 57  || c >= 97 && c <= 122 || c == 46){
                type = NUMBER;
                str.append(c);
            }else{
                if(str.length() != 0){
                    postfix.add(str.toString());
                    str.setLength(0);
                }
                switch(c){
                    case '+':
                    case '-':
                    case '*':
                    case '/':
                        if(type == 0 || type == OPERATOR || type == START_PAREN){
                            throw new RuntimeException("format error !");
                        }
                        type = OPERATOR;
                        operator(stack, c);
                        break;
                    case '(':
                        type = START_PAREN;
                        stack.push(c);
                        break;
                    case ')':
                        if(type == OPERATOR){
                            throw new RuntimeException("format error !");
                        }
                        type = END_PAREN;
                        endParen(stack);
                        break;
                }
            }
        }
        if(str.length() != 0){
            postfix.add(str.toString());
        }
        while(!stack.isEmpty()){
            char operator = stack.pop();
            if(operator == '('){
                throw new RuntimeException("paren not paired !");
            }
            postfix.add(operator + "");
        }
    }
    /**
     * 公式结果
     * @param variable 公式变量
     * @return 结果
     */
    public double getResult(double... variable){
        LinkedList<Double> stack = new LinkedList<Double>();
        int varIndex = 0;
        for(String element : postfix){
            char c = element.charAt(0);
            if(c >= 48 && c <= 57 || c == 46){
                stack.push(Double.parseDouble(element));
            }else if(c >= 97 && c <= 122){
                stack.push(variable[varIndex]);
                varIndex++;
            }else{
                double num2 = stack.pop();
                double num1 = stack.pop();
                double num;
                switch(c){
                    case '+':
                        num = add(num1,num2);
                        break;
                    case '-':
                        num = subtract(num1,num2);
                        break;
                    case '*':
                        num = multiply(num1,num2);
                        break;
                    case '/':
                        num = divide(num1,num2);
                        break;
                    default:
                        num = 0;
                }
                stack.push(num);
            }
        }
        if(stack.size() == 0){
            return 0;
        }else{
            return stack.pop();
        }
    }
    /**
     * 处理操作符
     * @param operator 操作符
     */
    private void operator(LinkedList<Character> stack, char operator){
        int prior = priority.get(operator);
        while(!stack.isEmpty()){
            char thisOperater = stack.pop();
            if(thisOperater == '('){
                stack.push(thisOperater);
                break;
            }else{
                int thisPrior = priority.get(thisOperater);
                if(thisPrior < prior){
                    stack.push(thisOperater);
                    break;
                }else{
                    postfix.add(thisOperater + "");
                }
            }
        }
        stack.push(operator);
    }
    /**
     * 处理结束括号
     */
    private void endParen(LinkedList<Character> stack){
        boolean startParen = false;
        while(!stack.isEmpty()){
            char element = stack.pop();
            if(element == '('){
                startParen = true;
                break;
            }else{
                postfix.add(element + "");
            }
        }
        if(startParen == false){
            throw new RuntimeException("paren not paired !");
        }
    }

    public static double add(double value1, double value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return b1.add(b2).doubleValue();
    }

    public static double subtract(double value1, double value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return b1.subtract(b2).doubleValue();
    }

    public static double multiply(double value1, double value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return b1.multiply(b2).doubleValue();
    }

    public static double divide(double value1, double value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return b1.divide(b2,4,BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 除法 四舍五入 小数点做多保留到14位
     * @param value1
     * @param value2
     * @return
     */
    public static double divide2(double value1, double value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return b1.divide(b2,14,4).doubleValue();
    }


    public static void main(String[] args) {
        SimpleFormulaAnalysis formula = new SimpleFormulaAnalysis("a/a");
        System.out.println(formula.getResult(3.98612768E8,1));
    }




}
